Micron Document
C.S.Burner 🪙 ━►🔥GIT Node


Displaying Raw • Download

meshchatx/src/frontend/components/docs/DocsPage.vue 9ec2a88817819d433c5d68dbec0ca070ca989b91 (9ec2a888) Text, 40.27 KB

T8b949e<!-- SPDX-License-Identifier: 0BSD -->

Tff7b72<templateTff7b72>
T8b949e<!-- eslint-disable vue/no-v-html -->
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffflex flex-col h-full bg-slate-50 dark:bg-zinc-950 overflow-hiddenTa5d6ff"Tff7b72>
T8b949e<!-- Header -->
Tff7b72<div
Te6edf3class=Ta5d6ff"Ta5d6ffp-2 md:p-3 border-b border-gray-200 dark:border-zinc-800 bg-white dark:bg-zinc-900 flex items-center gap-4 z-30Ta5d6ff"
Tff7b72>
T8b949e<!-- Title Section -->
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffflex items-center space-x-3 shrink-0Ta5d6ff"Tff7b72>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffhidden md:block p-2 bg-blue-100 dark:bg-blue-900/30 rounded-lgTa5d6ff"Tff7b72>
Tff7b72<MaterialDesignIcon
Te6edf3icon-name=Ta5d6ff"Ta5d6ffbook-open-variantTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffw-5 h-5 text-blue-600 dark:text-blue-400Ta5d6ff"
Tff7b72/>
Tff7b72</div>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffshrink-0Ta5d6ff"Tff7b72>
Tff7b72<h1 Te6edf3class=Ta5d6ff"Ta5d6fftext-xs md:text-sm font-bold text-gray-900 dark:text-zinc-100Ta5d6ff"Tff7b72>
{{ Te6edf3$t("docs.title") }}
Tff7b72</h1>
Tff7b72</div>
Tff7b72</div>

T8b949e<!-- Search & Navigation (Desktop) -->
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffhidden lg:flex flex-1 items-center gap-4 max-w-3xlTa5d6ff"Tff7b72>
T8b949e<!-- Tabs -->
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffflex bg-gray-100 dark:bg-zinc-800 p-0.5 rounded-lg shrink-0Ta5d6ff"Tff7b72>
Tff7b72<button
Te6edf3class=Ta5d6ff"Ta5d6ffpx-3 py-1 text-[10px] font-bold uppercase tracking-wider rounded-md transition-allTa5d6ff"
Te6edf3:class=Ta5d6ff"
Ta5d6ff activeTab === Ta5d6ff'Ta5d6ffmeshchatxTa5d6ff'
Ta5d6ff ? Ta5d6ff'Ta5d6ffbg-white dark:bg-zinc-700 text-blue-600 dark:text-blue-400 shadow-smTa5d6ff'
Ta5d6ff : Ta5d6ff'Ta5d6fftext-gray-500 hover:text-gray-700 dark:hover:text-zinc-300Ta5d6ff'
Ta5d6ff Ta5d6ff"
@Te6edf3click=Ta5d6ff"Ta5d6ffactiveTab = Ta5d6ff'Ta5d6ffmeshchatxTa5d6ff'Ta5d6ff"
Tff7b72>
MeshChatX
Tff7b72</button>
Tff7b72<button
Te6edf3class=Ta5d6ff"Ta5d6ffpx-3 py-1 text-[10px] font-bold uppercase tracking-wider rounded-md transition-allTa5d6ff"
Te6edf3:class=Ta5d6ff"
Ta5d6ff activeTab === Ta5d6ff'Ta5d6ffreticulumTa5d6ff'
Ta5d6ff ? Ta5d6ff'Ta5d6ffbg-white dark:bg-zinc-700 text-blue-600 dark:text-blue-400 shadow-smTa5d6ff'
Ta5d6ff : Ta5d6ff'Ta5d6fftext-gray-500 hover:text-gray-700 dark:hover:text-zinc-300Ta5d6ff'
Ta5d6ff Ta5d6ff"
@Te6edf3click=Ta5d6ff"Ta5d6ffactiveTab = Ta5d6ff'Ta5d6ffreticulumTa5d6ff'Ta5d6ff"
Tff7b72>
Reticulum
Tff7b72</button>
Tff7b72</div>

T8b949e<!-- Search Input -->
Tff7b72<div Te6edf3v-if=Ta5d6ff"Ta5d6ffstatus.has_docs || status.has_meshchatx_docsTa5d6ff" Te6edf3class=Ta5d6ff"Ta5d6ffrelative flex-1Ta5d6ff"Tff7b72>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffabsolute inset-y-0 left-0 pl-2.5 flex items-center pointer-events-noneTa5d6ff"Tff7b72>
Tff7b72<MaterialDesignIcon Te6edf3icon-name=Ta5d6ff"Ta5d6ffmagnifyTa5d6ff" Te6edf3class=Ta5d6ff"Ta5d6ffh-3.5 w-3.5 text-gray-400Ta5d6ff" Tff7b72/>
Tff7b72</div>
Tff7b72<input
Te6edf3v-model=Ta5d6ff"Ta5d6ffsearchQueryTa5d6ff"
Te6edf3type=Ta5d6ff"Ta5d6fftextTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffblock w-full pl-8 pr-8 py-1.5 border border-gray-200 dark:border-zinc-700 rounded-lg bg-gray-50 dark:bg-zinc-800 text-gray-900 dark:text-zinc-100 text-[11px] focus:outline-none focus:ring-2 focus:ring-blue-500/20 focus:border-blue-500 transition-allTa5d6ff"
Te6edf3placeholder=Ta5d6ff"Ta5d6ffSearch documentation...Ta5d6ff"
@Te6edf3input=Ta5d6ff"Ta5d6ffdebounceSearchTa5d6ff"
Tff7b72/>
Tff7b72<div Te6edf3v-if=Ta5d6ff"Ta5d6ffisSearchingTa5d6ff" Te6edf3class=Ta5d6ff"Ta5d6ffabsolute inset-y-0 right-0 pr-2.5 flex items-centerTa5d6ff"Tff7b72>
Tff7b72<MaterialDesignIcon Te6edf3icon-name=Ta5d6ff"Ta5d6ffloadingTa5d6ff" Te6edf3class=Ta5d6ff"Ta5d6ffh-3 w-3 text-gray-400 animate-spinTa5d6ff" Tff7b72/>
Tff7b72</div>
Tff7b72<button
Te6edf3v-else-if=Ta5d6ff"Ta5d6ffsearchQueryTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffabsolute inset-y-0 right-0 pr-2.5 flex items-centerTa5d6ff"
@Te6edf3click=Ta5d6ff"Ta5d6ffclearSearchTa5d6ff"
Tff7b72>
Tff7b72<MaterialDesignIcon
Te6edf3icon-name=Ta5d6ff"Ta5d6ffcloseTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffh-3 w-3 text-gray-400 hover:text-gray-600 dark:hover:text-zinc-200 cursor-pointerTa5d6ff"
Tff7b72/>
Tff7b72</button>
Tff7b72</div>
Tff7b72</div>

T8b949e<!-- Actions Section -->
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffflex items-center space-x-1 md:space-x-2 ml-auto shrink-0Ta5d6ff"Tff7b72>
T8b949e<!-- Version Selector -->
Tff7b72<div
Te6edf3v-if=Ta5d6ff"Ta5d6ffactiveTab === Ta5d6ff'Ta5d6ffreticulumTa5d6ff'Ta5d6ff && (status.has_docs || status.versions.length > 0)Ta5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffrelativeTa5d6ff"
Tff7b72>
Tff7b72<button
Te6edf3v-click-outside=Ta5d6ff"Ta5d6ff() => (showVersions = false)Ta5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffp-1.5 text-gray-500 hover:bg-gray-100 dark:hover:bg-zinc-800 rounded-lg transition-colors flex items-center gap-1.5Ta5d6ff"
Te6edf3:class=Ta5d6ff"Ta5d6ff{ Ta5d6ff'Ta5d6ffbg-gray-100 dark:bg-zinc-800Ta5d6ff'Ta5d6ff: showVersions }Ta5d6ff"
@Te6edf3click=Ta5d6ff"Ta5d6ffshowVersions = !showVersionsTa5d6ff"
Tff7b72>
Tff7b72<MaterialDesignIcon Te6edf3icon-name=Ta5d6ff"Ta5d6ffhistoryTa5d6ff" Te6edf3class=Ta5d6ff"Ta5d6ffw-4 h-4 md:w-5 md:h-5Ta5d6ff" Tff7b72/>
Tff7b72<span Te6edf3class=Ta5d6ff"Ta5d6ffhidden xl:inline text-[10px] font-bold uppercaseTa5d6ff"Tff7b72>{{
status.current_version || "Default"
}}Tff7b72</span>
Tff7b72</button>
Tff7b72<div
Te6edf3v-if=Ta5d6ff"Ta5d6ffshowVersionsTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffabsolute right-0 mt-2 w-48 bg-white dark:bg-zinc-800 border border-gray-200 dark:border-zinc-700 rounded-xl shadow-xl z-50 overflow-hiddenTa5d6ff"
Tff7b72>
Tff7b72<div
Te6edf3class=Ta5d6ff"Ta5d6ffp-2 border-b border-gray-100 dark:border-zinc-700 bg-gray-50/50 dark:bg-zinc-800/50Ta5d6ff"
Tff7b72>
Tff7b72<span Te6edf3class=Ta5d6ff"Ta5d6fftext-[10px] font-bold text-gray-400 uppercase tracking-widerTa5d6ff"Tff7b72>VersionsTff7b72</span>
Tff7b72</div>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffmax-h-64 overflow-y-auto py-1Ta5d6ff"Tff7b72>
Tff7b72<button
Te6edf3v-for=Ta5d6ff"Ta5d6ffversion in status.versionsTa5d6ff"
Te6edf3:key=Ta5d6ff"Ta5d6ffversionTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffw-full px-4 py-2 text-left text-[11px] hover:bg-blue-50 dark:hover:bg-blue-900/20 transition-colors flex items-center justify-between groupTa5d6ff"
Te6edf3:class=Ta5d6ff"
Ta5d6ff status.current_version === version
? Ta5d6ff'Ta5d6fftext-blue-600 dark:text-blue-400 font-boldTa5d6ff'
Ta5d6ff : Ta5d6ff'Ta5d6fftext-gray-700 dark:text-zinc-300Ta5d6ff'
Ta5d6ff Ta5d6ff"
@Te6edf3click=Ta5d6ff"Ta5d6ffswitchVersion(version)Ta5d6ff"
Tff7b72>
Tff7b72<span Te6edf3class=Ta5d6ff"Ta5d6fftruncateTa5d6ff"Tff7b72>{{ version }}Tff7b72</span>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffflex items-center space-x-1Ta5d6ff"Tff7b72>
Tff7b72<MaterialDesignIcon
Te6edf3v-if=Ta5d6ff"Ta5d6ffstatus.current_version === versionTa5d6ff"
Te6edf3icon-name=Ta5d6ff"Ta5d6ffcheckTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffw-3.5 h-3.5Ta5d6ff"
Tff7b72/>
Tff7b72<button
Te6edf3v-if=Ta5d6ff"Ta5d6ffstatus.versions.length > 1Ta5d6ff"
Te6edf3type=Ta5d6ff"Ta5d6ffbuttonTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffp-1 hover:text-red-500 opacity-0 group-hover:opacity-100 transition-opacityTa5d6ff"
Te6edf3title=Ta5d6ff"Ta5d6ffDelete this versionTa5d6ff"
@click.Te6edf3stop=Ta5d6ff"Ta5d6ffdeleteVersion(version)Ta5d6ff"
Tff7b72>
Tff7b72<MaterialDesignIcon Te6edf3icon-name=Ta5d6ff"Ta5d6ffdeleteTa5d6ff" Te6edf3class=Ta5d6ff"Ta5d6ffw-3.5 h-3.5Ta5d6ff" Tff7b72/>
Tff7b72</button>
Tff7b72</div>
Tff7b72</button>
Tff7b72<div
Te6edf3v-if=Ta5d6ff"Ta5d6ffstatus.versions.length === 0Ta5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffpx-4 py-3 text-center text-gray-500 text-[10px]Ta5d6ff"
Tff7b72>
No versions available
Tff7b72</div>
Tff7b72</div>
Tff7b72<div
Te6edf3class=Ta5d6ff"Ta5d6ffp-2 border-t border-gray-100 dark:border-zinc-700 bg-gray-50/50 dark:bg-zinc-800/50Ta5d6ff"
Tff7b72>
Tff7b72<label
Te6edf3class=Ta5d6ff"Ta5d6ffflex items-center justify-center gap-2 px-3 py-1.5 bg-blue-600 hover:bg-blue-700 text-white rounded-lg cursor-pointer transition-colors text-[10px] font-bold uppercaseTa5d6ff"
Tff7b72>
Tff7b72<MaterialDesignIcon Te6edf3icon-name=Ta5d6ff"Ta5d6ffuploadTa5d6ff" Te6edf3class=Ta5d6ff"Ta5d6ffw-3.5 h-3.5Ta5d6ff" Tff7b72/>
Tff7b72<spanTff7b72>Upload ZIPTff7b72</span>
Tff7b72<input Te6edf3type=Ta5d6ff"Ta5d6fffileTa5d6ff" Te6edf3accept=Ta5d6ff"Ta5d6ff.zipTa5d6ff" Te6edf3class=Ta5d6ff"Ta5d6ffhiddenTa5d6ff" @Te6edf3change=Ta5d6ff"Ta5d6ffhandleZipUploadTa5d6ff" Tff7b72/>
Tff7b72</label>
Tff7b72</div>
Tff7b72</div>
Tff7b72</div>

T8b949e<!-- Language Selector -->
Tff7b72<div Te6edf3v-if=Ta5d6ff"Ta5d6ffactiveTab === Ta5d6ff'Ta5d6ffreticulumTa5d6ff'Ta5d6ff && status.has_docsTa5d6ff" Te6edf3class=Ta5d6ff"Ta5d6ffrelativeTa5d6ff"Tff7b72>
Tff7b72<button
Te6edf3v-click-outside=Ta5d6ff"Ta5d6ff() => (showLanguages = false)Ta5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffp-1.5 text-gray-500 hover:bg-gray-100 dark:hover:bg-zinc-800 rounded-lg transition-colors flex items-center gap-1.5Ta5d6ff"
Te6edf3:class=Ta5d6ff"Ta5d6ff{ Ta5d6ff'Ta5d6ffbg-gray-100 dark:bg-zinc-800Ta5d6ff'Ta5d6ff: showLanguages }Ta5d6ff"
@Te6edf3click=Ta5d6ff"Ta5d6ffshowLanguages = !showLanguagesTa5d6ff"
Tff7b72>
Tff7b72<MaterialDesignIcon Te6edf3icon-name=Ta5d6ff"Ta5d6fftranslateTa5d6ff" Te6edf3class=Ta5d6ff"Ta5d6ffw-4 h-4 md:w-5 md:h-5Ta5d6ff" Tff7b72/>
Tff7b72<span Te6edf3class=Ta5d6ff"Ta5d6ffhidden xl:inline text-[10px] font-bold uppercaseTa5d6ff"Tff7b72>{{ currentLang }}Tff7b72</span>
Tff7b72</button>
Tff7b72<div
Te6edf3v-if=Ta5d6ff"Ta5d6ffshowLanguagesTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffabsolute right-0 top-full mt-1 bg-white dark:bg-zinc-900 border border-gray-200 dark:border-zinc-800 rounded-lg shadow-xl p-1 min-w-[120px] z-20Ta5d6ff"
Tff7b72>
Tff7b72<button
Te6edf3v-for=Ta5d6ff"Ta5d6fflang in allLanguagesTa5d6ff"
Te6edf3:key=Ta5d6ff"Ta5d6fflang.codeTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffflex items-center w-full px-3 py-2 text-[10px] font-bold uppercase hover:bg-gray-50 dark:hover:bg-zinc-800 rounded-md transition-colorsTa5d6ff"
Te6edf3:class=Ta5d6ff"Ta5d6fflang.code === currentLang ? Ta5d6ff'Ta5d6fftext-blue-500Ta5d6ff'Ta5d6ff : Ta5d6ff'Ta5d6fftext-gray-600 dark:text-zinc-400Ta5d6ff'Ta5d6ff"
@Te6edf3click=Ta5d6ff"Ta5d6ffsetLanguage(lang.code)Ta5d6ff"
Tff7b72>
{{ lang.name }} ({{ lang.code }})
Tff7b72</button>
Tff7b72</div>
Tff7b72</div>

T8b949e<!-- Export Button -->
Tff7b72<button
Te6edf3v-if=Ta5d6ff"Ta5d6ffstatus.has_docs || status.has_meshchatx_docsTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffp-1.5 text-gray-500 hover:bg-gray-100 dark:hover:bg-zinc-800 rounded-lg transition-colorsTa5d6ff"
Te6edf3title=Ta5d6ff"Ta5d6ffExport all documentation as ZIPTa5d6ff"
@Te6edf3click=Ta5d6ff"Ta5d6ffexportDocsTa5d6ff"
Tff7b72>
Tff7b72<MaterialDesignIcon Te6edf3icon-name=Ta5d6ff"Ta5d6ffdownloadTa5d6ff" Te6edf3class=Ta5d6ff"Ta5d6ffw-4 h-4 md:w-5 md:h-5Ta5d6ff" Tff7b72/>
Tff7b72</button>

T8b949e<!-- Share Reticulum Manual (re-uploadable ZIP) -->
Tff7b72<button
Te6edf3v-if=Ta5d6ff"Ta5d6ffstatus.has_docsTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffp-1.5 text-gray-500 hover:bg-gray-100 dark:hover:bg-zinc-800 rounded-lg transition-colorsTa5d6ff"
Te6edf3:title=Ta5d6ff"Te6edf3$tTa5d6ff(Ta5d6ff'Ta5d6ffdocs.btn_shareTa5d6ff'Ta5d6ff)Ta5d6ff"
@Te6edf3click=Ta5d6ff"Ta5d6ffexportReticulumDocsTa5d6ff"
Tff7b72>
Tff7b72<MaterialDesignIcon Te6edf3icon-name=Ta5d6ff"Ta5d6ffshare-variantTa5d6ff" Te6edf3class=Ta5d6ff"Ta5d6ffw-4 h-4 md:w-5 md:h-5Ta5d6ff" Tff7b72/>
Tff7b72</button>

T8b949e<!-- Upload Custom Manual -->
Tff7b72<label
Te6edf3:class=Ta5d6ff"Ta5d6ff{ Ta5d6ff'Ta5d6ffopacity-50 pointer-events-noneTa5d6ff'Ta5d6ff: status.status === Ta5d6ff'Ta5d6ffextractingTa5d6ff'Ta5d6ff }Ta5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffp-1.5 text-gray-500 hover:bg-gray-100 dark:hover:bg-zinc-800 rounded-lg transition-colors cursor-pointerTa5d6ff"
Te6edf3:title=Ta5d6ff"Te6edf3$tTa5d6ff(Ta5d6ff'Ta5d6ffdocs.btn_uploadTa5d6ff'Ta5d6ff)Ta5d6ff"
Tff7b72>
Tff7b72<MaterialDesignIcon
Te6edf3:icon-name=Ta5d6ff"Ta5d6ffstatus.status === Ta5d6ff'Ta5d6ffextractingTa5d6ff'Ta5d6ff ? Ta5d6ff'Ta5d6ffloadingTa5d6ff'Ta5d6ff : Ta5d6ff'Ta5d6ffuploadTa5d6ff'Ta5d6ff"
Te6edf3:class=Ta5d6ff"Ta5d6ff{ Ta5d6ff'Ta5d6ffanimate-spinTa5d6ff'Ta5d6ff: status.status === Ta5d6ff'Ta5d6ffextractingTa5d6ff'Ta5d6ff }Ta5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffw-4 h-4 md:w-5 md:h-5Ta5d6ff"
Tff7b72/>
Tff7b72<input Te6edf3type=Ta5d6ff"Ta5d6fffileTa5d6ff" Te6edf3accept=Ta5d6ff"Ta5d6ff.zipTa5d6ff" Te6edf3class=Ta5d6ff"Ta5d6ffhiddenTa5d6ff" @Te6edf3change=Ta5d6ff"Ta5d6ffhandleZipUploadTa5d6ff" Tff7b72/>
Tff7b72</label>

T8b949e<!-- Open External -->
Tff7b72<a
Te6edf3v-if=Ta5d6ff"Ta5d6ffstatus.has_docsTa5d6ff"
Te6edf3:href=Ta5d6ff"Ta5d6fflocalDocsUrlTa5d6ff"
Te6edf3target=Ta5d6ff"Ta5d6ff_blankTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffhidden sm:flex items-center px-2.5 py-1.5 bg-zinc-900 dark:bg-white text-white dark:text-zinc-900 rounded-lg hover:opacity-90 transition-opacity font-bold text-[10px] shadow-smTa5d6ff"
Tff7b72>
Tff7b72<MaterialDesignIcon Te6edf3icon-name=Ta5d6ff"Ta5d6ffopen-in-newTa5d6ff" Te6edf3class=Ta5d6ff"Ta5d6ffw-3 h-3 mr-1.5Ta5d6ff" Tff7b72/>
Open
Tff7b72</a>
Tff7b72</div>
Tff7b72</div>

T8b949e<!-- Secondary Navigation (Mobile/Tablet) -->
Tff7b72<div
Te6edf3v-if=Ta5d6ff"Ta5d6ff(status.has_docs || status.has_meshchatx_docs) && !isSearchingTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6fflg:hidden px-3 py-2 bg-white dark:bg-zinc-900 border-b border-gray-200 dark:border-zinc-800 z-10Ta5d6ff"
Tff7b72>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffflex flex-col lg:flex-row items-center gap-2 w-fullTa5d6ff"Tff7b72>
T8b949e<!-- Tabs -->
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffflex bg-gray-100 dark:bg-zinc-800 p-0.5 rounded-lg w-full md:w-autoTa5d6ff"Tff7b72>
Tff7b72<button
Te6edf3class=Ta5d6ff"Ta5d6ffflex-1 md:flex-none px-4 py-1.5 text-[10px] font-bold uppercase tracking-wider rounded-md transition-allTa5d6ff"
Te6edf3:class=Ta5d6ff"
Ta5d6ff activeTab === Ta5d6ff'Ta5d6ffmeshchatxTa5d6ff'
Ta5d6ff ? Ta5d6ff'Ta5d6ffbg-white dark:bg-zinc-700 text-blue-600 dark:text-blue-400 shadow-smTa5d6ff'
Ta5d6ff : Ta5d6ff'Ta5d6fftext-gray-500 hover:text-gray-700 dark:hover:text-zinc-300Ta5d6ff'
Ta5d6ff Ta5d6ff"
@Te6edf3click=Ta5d6ff"Ta5d6ffactiveTab = Ta5d6ff'Ta5d6ffmeshchatxTa5d6ff'Ta5d6ff"
Tff7b72>
MeshChatX
Tff7b72</button>
Tff7b72<button
Te6edf3class=Ta5d6ff"Ta5d6ffflex-1 md:flex-none px-4 py-1.5 text-[10px] font-bold uppercase tracking-wider rounded-md transition-allTa5d6ff"
Te6edf3:class=Ta5d6ff"
Ta5d6ff activeTab === Ta5d6ff'Ta5d6ffreticulumTa5d6ff'
Ta5d6ff ? Ta5d6ff'Ta5d6ffbg-white dark:bg-zinc-700 text-blue-600 dark:text-blue-400 shadow-smTa5d6ff'
Ta5d6ff : Ta5d6ff'Ta5d6fftext-gray-500 hover:text-gray-700 dark:hover:text-zinc-300Ta5d6ff'
Ta5d6ff Ta5d6ff"
@Te6edf3click=Ta5d6ff"Ta5d6ffactiveTab = Ta5d6ff'Ta5d6ffreticulumTa5d6ff'Ta5d6ff"
Tff7b72>
Reticulum
Tff7b72</button>
Tff7b72</div>

T8b949e<!-- Search Input -->
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffrelative w-fullTa5d6ff"Tff7b72>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffabsolute inset-y-0 left-0 pl-3 flex items-center pointer-events-noneTa5d6ff"Tff7b72>
Tff7b72<MaterialDesignIcon Te6edf3icon-name=Ta5d6ff"Ta5d6ffmagnifyTa5d6ff" Te6edf3class=Ta5d6ff"Ta5d6ffh-3.5 w-3.5 text-gray-400Ta5d6ff" Tff7b72/>
Tff7b72</div>
Tff7b72<input
Te6edf3v-model=Ta5d6ff"Ta5d6ffsearchQueryTa5d6ff"
Te6edf3type=Ta5d6ff"Ta5d6fftextTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffblock w-full pl-9 pr-9 py-2 border border-gray-200 dark:border-zinc-700 rounded-lg bg-gray-50 dark:bg-zinc-800 text-gray-900 dark:text-zinc-100 text-xs focus:outline-none focus:ring-2 focus:ring-blue-500/20 focus:border-blue-500 transition-allTa5d6ff"
Te6edf3placeholder=Ta5d6ff"Ta5d6ffSearch all documentation...Ta5d6ff"
@Te6edf3input=Ta5d6ff"Ta5d6ffdebounceSearchTa5d6ff"
Tff7b72/>
Tff7b72<div Te6edf3v-if=Ta5d6ff"Ta5d6ffisSearchingTa5d6ff" Te6edf3class=Ta5d6ff"Ta5d6ffabsolute inset-y-0 right-0 pr-3 flex items-centerTa5d6ff"Tff7b72>
Tff7b72<MaterialDesignIcon Te6edf3icon-name=Ta5d6ff"Ta5d6ffloadingTa5d6ff" Te6edf3class=Ta5d6ff"Ta5d6ffh-3 w-3 text-gray-400 animate-spinTa5d6ff" Tff7b72/>
Tff7b72</div>
Tff7b72<button
Te6edf3v-else-if=Ta5d6ff"Ta5d6ffsearchQueryTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffabsolute inset-y-0 right-0 pr-3 flex items-centerTa5d6ff"
@Te6edf3click=Ta5d6ff"Ta5d6ffclearSearchTa5d6ff"
Tff7b72>
Tff7b72<MaterialDesignIcon
Te6edf3icon-name=Ta5d6ff"Ta5d6ffcloseTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffh-3 w-3 text-gray-400 hover:text-gray-600 dark:hover:text-zinc-200 cursor-pointerTa5d6ff"
Tff7b72/>
Tff7b72</button>
Tff7b72</div>
Tff7b72</div>
Tff7b72</div>

T8b949e<!-- Progress Bar -->
Tff7b72<div
Te6edf3v-if=Ta5d6ff"Ta5d6ffstatus.status === Ta5d6ff'Ta5d6ffextractingTa5d6ff'Ta5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffw-full h-1 bg-gray-200 dark:bg-zinc-800 overflow-hidden relativeTa5d6ff"
Tff7b72>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffbg-blue-500 h-full transition-all duration-300Ta5d6ff" Te6edf3:style=Ta5d6ff"Ta5d6ff{ width: status.progress + Ta5d6ff'Ta5d6ff%Ta5d6ff'Ta5d6ff }Ta5d6ff"Tff7b72>Tff7b72</div>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffabsolute inset-0 bg-blue-500/30 animate-pulseTa5d6ff"Tff7b72>Tff7b72</div>
Tff7b72</div>

T8b949e<!-- Main Content (Iframe or Search Results) -->
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffflex-1 relative bg-white dark:bg-zinc-900 overflow-hiddenTa5d6ff"Tff7b72>
T8b949e<!-- Search Results Overlay -->
Tff7b72<div
Te6edf3v-if=Ta5d6ff"Ta5d6ffsearchResults.length > 0 && searchQueryTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffabsolute inset-0 z-20 bg-white dark:bg-zinc-900 overflow-y-autoTa5d6ff"
Tff7b72>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffmax-w-2xl mx-auto p-6 space-y-6Ta5d6ff"Tff7b72>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffflex items-center justify-between px-2Ta5d6ff"Tff7b72>
Tff7b72<h2 Te6edf3class=Ta5d6ff"Ta5d6fftext-[10px] font-bold text-gray-400 uppercase tracking-widestTa5d6ff"Tff7b72>Search ResultsTff7b72</h2>
Tff7b72<span
Te6edf3class=Ta5d6ff"Ta5d6fftext-[10px] font-bold text-blue-500 px-2 py-0.5 bg-blue-50 dark:bg-blue-900/20 rounded-fullTa5d6ff"
Tff7b72>{{ searchResults.length }} matchesTff7b72</span
>
Tff7b72</div>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffspace-y-2Ta5d6ff"Tff7b72>
Tff7b72<div
Te6edf3v-for=Ta5d6ff"Ta5d6ffresult in searchResultsTa5d6ff"
Te6edf3:key=Ta5d6ff"Ta5d6ffresult.pathTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffgroup p-4 hover:bg-gray-50 dark:hover:bg-zinc-800/50 rounded-2xl cursor-pointer transition-colors border border-gray-100 dark:border-zinc-800/50 hover:border-blue-200 dark:hover:border-blue-900/30Ta5d6ff"
@Te6edf3click=Ta5d6ff"Ta5d6ffnavigateTo(result.path)Ta5d6ff"
Tff7b72>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffflex items-start justify-between gap-4Ta5d6ff"Tff7b72>
Tff7b72<div
Te6edf3class=Ta5d6ff"Ta5d6fffont-bold text-sm text-gray-900 dark:text-zinc-100 group-hover:text-blue-600 dark:group-hover:text-blue-400 transition-colorsTa5d6ff"
Tff7b72>
{{ result.title }}
Tff7b72</div>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffflex items-center space-x-2Ta5d6ff"Tff7b72>
Tff7b72<span
Te6edf3class=Ta5d6ff"Ta5d6ffpx-1.5 py-0.5 rounded bg-gray-100 dark:bg-zinc-800 text-[8px] font-bold text-gray-500 uppercase tracking-tighterTa5d6ff"
Tff7b72>
{{ result.source }}
Tff7b72</span>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6fftext-[9px] text-gray-400 uppercase font-mono mt-0.5 shrink-0Ta5d6ff"Tff7b72>
{{ result.path.split("/").pop() }}
Tff7b72</div>
Tff7b72</div>
Tff7b72</div>
Tff7b72<p
Te6edf3class=Ta5d6ff"Ta5d6ffmt-1.5 text-xs text-gray-600 dark:text-zinc-400 line-clamp-3 leading-relaxedTa5d6ff"
Te6edf3v-html=Ta5d6ff"Ta5d6ffhighlightMatch(result.snippet)Ta5d6ff"
Tff7b72>Tff7b72</p>
Tff7b72</div>
Tff7b72</div>
Tff7b72</div>
Tff7b72</div>

T8b949e<!-- No Results State -->
Tff7b72<div
Te6edf3v-if=Ta5d6ff"Ta5d6ffsearchQuery && !isSearching && searchResults.length === 0Ta5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffabsolute inset-0 z-20 bg-white dark:bg-zinc-900 flex flex-col items-center justify-center p-8 text-centerTa5d6ff"
Tff7b72>
Tff7b72<div
Te6edf3class=Ta5d6ff"Ta5d6ffw-16 h-16 bg-gray-50 dark:bg-zinc-800/50 rounded-full flex items-center justify-center mb-4Ta5d6ff"
Tff7b72>
Tff7b72<MaterialDesignIcon Te6edf3icon-name=Ta5d6ff"Ta5d6fftext-searchTa5d6ff" Te6edf3class=Ta5d6ff"Ta5d6ffw-8 h-8 text-gray-300 dark:text-zinc-600Ta5d6ff" Tff7b72/>
Tff7b72</div>
Tff7b72<h3 Te6edf3class=Ta5d6ff"Ta5d6fftext-sm font-medium text-gray-900 dark:text-zinc-100Ta5d6ff"Tff7b72>No results foundTff7b72</h3>
Tff7b72<p Te6edf3class=Ta5d6ff"Ta5d6fftext-xs text-gray-500 dark:text-zinc-400 mt-1Ta5d6ff"Tff7b72>Try different keywords or check spelling.Tff7b72</p>
Tff7b72<button
Te6edf3class=Ta5d6ff"Ta5d6ffmt-4 text-xs font-bold text-blue-500 hover:text-blue-600 transition-colorsTa5d6ff"
@Te6edf3click=Ta5d6ff"Ta5d6ffclearSearchTa5d6ff"
Tff7b72>
Clear Search
Tff7b72</button>
Tff7b72</div>

Tff7b72<div
Te6edf3v-if=Ta5d6ff"Ta5d6ffstatus.last_errorTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffabsolute inset-0 z-10 flex items-center justify-center p-6 bg-white/90 dark:bg-zinc-900/90 backdrop-blur-smTa5d6ff"
Tff7b72>
Tff7b72<div
Te6edf3class=Ta5d6ff"Ta5d6ffmax-w-md w-full p-6 bg-red-50 dark:bg-red-900/20 border border-red-100 dark:border-red-900/30 rounded-2xl text-red-600 dark:text-red-400 text-center shadow-xlTa5d6ff"
Tff7b72>
Tff7b72<MaterialDesignIcon Te6edf3icon-name=Ta5d6ff"Ta5d6ffalert-circle-outlineTa5d6ff" Te6edf3class=Ta5d6ff"Ta5d6ffw-12 h-12 mx-auto mb-3Ta5d6ff" Tff7b72/>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6fftext-lg font-bold mb-2Ta5d6ff"Tff7b72>{{ Te6edf3$t("docs.error") }}Tff7b72</div>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6fftext-sm opacity-80Ta5d6ff"Tff7b72>{{ status.last_error }}Tff7b72</div>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffflex flex-col gap-4 mt-6Ta5d6ff"Tff7b72>
Tff7b72<label
Te6edf3class=Ta5d6ff"Ta5d6ffw-full px-6 py-2.5 bg-zinc-900 dark:bg-white text-white dark:text-zinc-900 rounded-xl text-xs font-bold hover:opacity-90 transition-opacity cursor-pointer flex items-center justify-center gap-2Ta5d6ff"
Tff7b72>
Tff7b72<MaterialDesignIcon Te6edf3icon-name=Ta5d6ff"Ta5d6ffuploadTa5d6ff" Te6edf3class=Ta5d6ff"Ta5d6ffw-3.5 h-3.5Ta5d6ff" Tff7b72/>
Tff7b72<spanTff7b72>{{ Te6edf3$t("docs.btn_upload") }}Tff7b72</span>
Tff7b72<input Te6edf3type=Ta5d6ff"Ta5d6fffileTa5d6ff" Te6edf3accept=Ta5d6ff"Ta5d6ff.zipTa5d6ff" Te6edf3class=Ta5d6ff"Ta5d6ffhiddenTa5d6ff" @Te6edf3change=Ta5d6ff"Ta5d6ffhandleZipUploadTa5d6ff" Tff7b72/>
Tff7b72</label>
Tff7b72<button
Te6edf3class=Ta5d6ff"Ta5d6fftext-[10px] font-bold text-red-500/60 hover:text-red-500 uppercase tracking-widest transition-colorsTa5d6ff"
@Te6edf3click=Ta5d6ff"Ta5d6ffdismissErrorTa5d6ff"
Tff7b72>
Dismiss
Tff7b72</button>
Tff7b72</div>
Tff7b72</div>
Tff7b72</div>

Tff7b72<div
Te6edf3v-if=Ta5d6ff"Ta5d6ffstatus.status === Ta5d6ff'Ta5d6ffextractingTa5d6ff'Ta5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffabsolute inset-0 z-10 flex flex-col items-center justify-center bg-white/80 dark:bg-zinc-900/80 backdrop-blur-mdTa5d6ff"
Tff7b72>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffrelative w-24 h-24 mb-6Ta5d6ff"Tff7b72>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffabsolute inset-0 border-4 border-blue-100 dark:border-blue-900/30 rounded-fullTa5d6ff"Tff7b72>Tff7b72</div>
Tff7b72<div
Te6edf3class=Ta5d6ff"Ta5d6ffabsolute inset-0 border-4 border-blue-600 rounded-full transition-all duration-300Ta5d6ff"
Te6edf3:style=Ta5d6ff"Ta5d6ff{ clipPath: `inset(0 0 0 0)`, transform: `rotate(Tff7b72${Te6edf3statusTff7b72.Td2a8ffprogress Tff7b72* T79c0ff3.6Tff7b72}Ta5d6ffdeg)` }Ta5d6ff"
Te6edf3style=Ta5d6ff"Ta5d6ffborder-color: transparent; border-top-color: currentColorTa5d6ff"
Tff7b72>Tff7b72</div>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffabsolute inset-0 flex items-center justify-centerTa5d6ff"Tff7b72>
Tff7b72<MaterialDesignIcon
Te6edf3icon-name=Ta5d6ff"Ta5d6fffolder-zip-outlineTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffw-10 h-10 text-blue-600 animate-bounceTa5d6ff"
Tff7b72/>
Tff7b72</div>
Tff7b72</div>
Tff7b72<h3 Te6edf3class=Ta5d6ff"Ta5d6fftext-lg font-bold text-gray-900 dark:text-zinc-100 mb-1Ta5d6ff"Tff7b72>
{{ Te6edf3$t("docs.status_extracting") }}
Tff7b72</h3>
Tff7b72<p Te6edf3class=Ta5d6ff"Ta5d6fftext-sm text-gray-500 dark:text-zinc-400Ta5d6ff"Tff7b72>{{ status.progress }}% CompleteTff7b72</p>
Tff7b72</div>

T8b949e<!-- MeshChatX Docs View -->
Tff7b72<div Te6edf3v-if=Ta5d6ff"Ta5d6ffactiveTab === Ta5d6ff'Ta5d6ffmeshchatxTa5d6ff'Ta5d6ff && !searchQueryTa5d6ff" Te6edf3class=Ta5d6ff"Ta5d6ffflex h-full overflow-hiddenTa5d6ff"Tff7b72>
T8b949e<!-- Doc Sidebar (mobile hidden) -->
Tff7b72<div
Te6edf3class=Ta5d6ff"Ta5d6ffhidden md:flex flex-col w-64 border-r border-gray-200 dark:border-zinc-800 bg-gray-50/50 dark:bg-zinc-900/50Ta5d6ff"
Tff7b72>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffp-4 border-b border-gray-200 dark:border-zinc-800Ta5d6ff"Tff7b72>
Tff7b72<h3 Te6edf3class=Ta5d6ff"Ta5d6fftext-[10px] font-bold text-gray-400 uppercase tracking-widestTa5d6ff"Tff7b72>MeshChatX DocsTff7b72</h3>
Tff7b72</div>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffflex-1 overflow-y-auto p-2 space-y-1Ta5d6ff"Tff7b72>
Tff7b72<button
Te6edf3v-for=Ta5d6ff"Ta5d6ffdoc in meshchatxDocsTa5d6ff"
Te6edf3:key=Ta5d6ff"Ta5d6ffdoc.pathTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffw-full text-left px-3 py-2 rounded-xl text-xs transition-all flex items-center space-x-3Ta5d6ff"
Te6edf3:class=Ta5d6ff"
Ta5d6ff selectedDocPath === doc.path
? Ta5d6ff'Ta5d6ffbg-blue-50 dark:bg-blue-900/20 text-blue-600 dark:text-blue-400 font-bold shadow-smTa5d6ff'
Ta5d6ff : Ta5d6ff'Ta5d6fftext-gray-600 dark:text-zinc-400 hover:bg-white dark:hover:bg-zinc-800Ta5d6ff'
Ta5d6ff Ta5d6ff"
@Te6edf3click=Ta5d6ff"Ta5d6ffselectDoc(doc.path)Ta5d6ff"
Tff7b72>
Tff7b72<MaterialDesignIcon
Te6edf3:icon-name=Ta5d6ff"Ta5d6ffdoc.type === Ta5d6ff'Ta5d6ffmarkdownTa5d6ff'Ta5d6ff ? Ta5d6ff'Ta5d6fflanguage-markdownTa5d6ff'Ta5d6ff : Ta5d6ff'Ta5d6fffile-document-outlineTa5d6ff'Ta5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffw-4 h-4Ta5d6ff"
Tff7b72/>
Tff7b72<span Te6edf3class=Ta5d6ff"Ta5d6fftruncateTa5d6ff"Tff7b72>{{ (doc.name || "").replace(/\.(md|txt)$/, "") }}Tff7b72</span>
Tff7b72</button>
Tff7b72</div>
Tff7b72</div>

T8b949e<!-- Doc Content -->
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffflex-1 flex flex-col bg-white dark:bg-zinc-900 overflow-hidden relativeTa5d6ff"Tff7b72>
T8b949e<!-- Mobile Selector -->
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffmd:hidden p-3 border-b border-gray-200 dark:border-zinc-800Ta5d6ff"Tff7b72>
Tff7b72<select
Te6edf3v-model=Ta5d6ff"Ta5d6ffselectedDocPathTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffw-full bg-gray-50 dark:bg-zinc-800 border-none rounded-lg text-xs font-bold p-2Ta5d6ff"
@Te6edf3change=Ta5d6ff"Ta5d6ffselectDoc(selectedDocPath)Ta5d6ff"
Tff7b72>
Tff7b72<option Te6edf3v-for=Ta5d6ff"Ta5d6ffdoc in meshchatxDocsTa5d6ff" Te6edf3:key=Ta5d6ff"Ta5d6ffdoc.pathTa5d6ff" Te6edf3:value=Ta5d6ff"Ta5d6ffdoc.pathTa5d6ff"Tff7b72>
{{ (doc.name || "").replace(/\.(md|txt)$/, "") }}
Tff7b72</option>
Tff7b72</select>
Tff7b72</div>

Tff7b72<div Te6edf3v-if=Ta5d6ff"Ta5d6ffselectedDocContentTa5d6ff" Te6edf3class=Ta5d6ff"Ta5d6ffflex-1 overflow-y-auto p-6 md:p-10 scroll-smoothTa5d6ff"Tff7b72>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffmax-w-3xl mx-autoTa5d6ff"Tff7b72>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffmax-w-none break-wordsTa5d6ff" Te6edf3v-html=Ta5d6ff"Ta5d6ffselectedDocContent.htmlTa5d6ff"Tff7b72>Tff7b72</div>
Tff7b72</div>
Tff7b72</div>
Tff7b72<div
Te6edf3v-else-if=Ta5d6ff"Ta5d6ffmeshchatxDocs.length > 0Ta5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffflex-1 flex flex-col items-center justify-center p-8 text-center opacity-50Ta5d6ff"
Tff7b72>
Tff7b72<MaterialDesignIcon Te6edf3icon-name=Ta5d6ff"Ta5d6ffbook-open-outlineTa5d6ff" Te6edf3class=Ta5d6ff"Ta5d6ffw-12 h-12 mb-4 text-gray-300Ta5d6ff" Tff7b72/>
Tff7b72<h3 Te6edf3class=Ta5d6ff"Ta5d6fftext-sm font-boldTa5d6ff"Tff7b72>Select a document to readTff7b72</h3>
Tff7b72</div>
Tff7b72<div v-else Te6edf3class=Ta5d6ff"Ta5d6ffflex-1 flex flex-col items-center justify-center p-8 text-center opacity-50Ta5d6ff"Tff7b72>
Tff7b72<MaterialDesignIcon Te6edf3icon-name=Ta5d6ff"Ta5d6ffalert-circle-outlineTa5d6ff" Te6edf3class=Ta5d6ff"Ta5d6ffw-12 h-12 mb-4 text-gray-300Ta5d6ff" Tff7b72/>
Tff7b72<h3 Te6edf3class=Ta5d6ff"Ta5d6fftext-sm font-boldTa5d6ff"Tff7b72>No MeshChatX docs foundTff7b72</h3>
Tff7b72<p Te6edf3class=Ta5d6ff"Ta5d6fftext-xs mt-1Ta5d6ff"Tff7b72>Place .md or .txt files in your docs folder.Tff7b72</p>
Tff7b72</div>
Tff7b72</div>
Tff7b72</div>

T8b949e<!-- Reticulum Docs View -->
Tff7b72<iframe
Te6edf3v-if=Ta5d6ff"Ta5d6ffactiveTab === Ta5d6ff'Ta5d6ffreticulumTa5d6ff'Ta5d6ff && status.has_docs && !searchQueryTa5d6ff"
Te6edf3:key=Ta5d6ff"Ta5d6fflocalDocsUrlTa5d6ff"
Te6edf3ref=Ta5d6ff"Ta5d6ffdocsFrameTa5d6ff"
Te6edf3:src=Ta5d6ff"Ta5d6fflocalDocsUrlTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffw-full h-full border-none opacity-0 transition-opacity duration-1000Ta5d6ff"
@Te6edf3load=Ta5d6ff"Te6edf3$el.querySelectorTa5d6ff(Ta5d6ff'Ta5d6ffiframeTa5d6ff'Ta5d6ff).style.opacity = Ta5d6ff'Ta5d6ff1Ta5d6ff'Ta5d6ff"
Tff7b72>Tff7b72</iframe>

Tff7b72<div
Te6edf3v-else-if=Ta5d6ff"Ta5d6ffstatus.status !== Ta5d6ff'Ta5d6ffextractingTa5d6ff'Ta5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffh-full flex flex-col items-center justify-center p-8 text-center space-y-4Ta5d6ff"
Tff7b72>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffw-16 h-16 bg-gray-50 dark:bg-zinc-800/50 rounded-full flex items-center justify-centerTa5d6ff"Tff7b72>
Tff7b72<MaterialDesignIcon Te6edf3icon-name=Ta5d6ff"Ta5d6ffbook-outlineTa5d6ff" Te6edf3class=Ta5d6ff"Ta5d6ffw-8 h-8 text-gray-300 dark:text-zinc-600Ta5d6ff" Tff7b72/>
Tff7b72</div>
Tff7b72<divTff7b72>
Tff7b72<h3 Te6edf3class=Ta5d6ff"Ta5d6fftext-sm font-medium text-gray-900 dark:text-zinc-100Ta5d6ff"Tff7b72>Reticulum ManualTff7b72</h3>
Tff7b72<p Te6edf3class=Ta5d6ff"Ta5d6fftext-xs text-gray-500 dark:text-zinc-400 mt-1 max-w-[260px]Ta5d6ff"Tff7b72>
{{ Te6edf3$t("docs.empty_state_hint") }}
Tff7b72</p>
Tff7b72</div>
Tff7b72<label
Te6edf3class=Ta5d6ff"Ta5d6ffpx-6 py-2 bg-blue-600 text-white rounded-full text-xs font-bold hover:bg-blue-700 transition-colors shadow-lg shadow-blue-500/20 cursor-pointer flex items-center gap-2Ta5d6ff"
Tff7b72>
Tff7b72<MaterialDesignIcon Te6edf3icon-name=Ta5d6ff"Ta5d6ffuploadTa5d6ff" Te6edf3class=Ta5d6ff"Ta5d6ffw-3.5 h-3.5Ta5d6ff" Tff7b72/>
Tff7b72<spanTff7b72>{{ Te6edf3$t("docs.btn_upload") }}Tff7b72</span>
Tff7b72<input Te6edf3type=Ta5d6ff"Ta5d6fffileTa5d6ff" Te6edf3accept=Ta5d6ff"Ta5d6ff.zipTa5d6ff" Te6edf3class=Ta5d6ff"Ta5d6ffhiddenTa5d6ff" @Te6edf3change=Ta5d6ff"Ta5d6ffhandleZipUploadTa5d6ff" Tff7b72/>
Tff7b72</label>
Tff7b72</div>
Tff7b72</div>
Tff7b72</div>
Tff7b72</template>

Tff7b72<scriptTff7b72>
import MaterialDesignIcon from "../MaterialDesignIcon.vue";
import ToastUtils from "../../js/ToastUtils";
import GlobalState from "../../js/GlobalState";
import { bundledReticulumDocsUrl } from "../../js/reticulumDocsEntryUrl.js";

export default {
components: {
MaterialDesignIcon,
},
data() {
return {
status: {
status: "idle",
progress: 0,
last_error: null,
has_docs: false,
has_meshchatx_docs: false,
versions: [],
current_version: null,
},
statusInterval: null,
showLanguages: false,
showVersions: false,
searchQuery: "",
searchResults: [],
isSearching: false,
searchTimeout: null,
activeTab: "meshchatx",
meshchatxDocs: [],
selectedDocPath: null,
selectedDocContent: null,
selectedReticulumPath: null,
languages: {
en: "English",
de: "Deutsch",
es: "Español",
jp: "日本語",
nl: "Nederlands",
pl: "Polski",
"pt-br": "Português",
tr: "Türkçe",
uk: "Українська",
"zh-cn": "简体中文",
},
};
},
computed: {
currentLang() {
return this.$i18n.locale;
},
localDocsUrl() {
if (this.selectedReticulumPath) {
return `/reticulum-docs/${this.selectedReticulumPath}`;
}
return bundledReticulumDocsUrl(this.currentLang);
},
allLanguages() {
return Object.entries(this.languages).map(([code, name]) => ({
code,
name,
}));
},
otherLanguages() {
if (!this.status.has_docs) return [];
return this.allLanguages.filter((l) => l.code !== this.currentLang);
},
},
mounted() {
this.fetchStatus();
this.fetchMeshChatXDocs();
this.statusInterval = setInterval(this.fetchStatus, 2000);
},
beforeUnmount() {
if (this.statusInterval) {
clearInterval(this.statusInterval);
}
},
methods: {
async fetchStatus() {
try {
const response = await window.api.get("/api/v1/docs/status");
this.status = response.data;

if (!this.status.has_docs && this.status.has_meshchatx_docs && this.activeTab === "reticulum") {
this.activeTab = "meshchatx";
} else if (this.status.has_docs && !this.status.has_meshchatx_docs && this.activeTab === "meshchatx") {
this.activeTab = "reticulum";
}
} catch (error) {
console.error("Failed to fetch docs status:", error);
}
},
dismissError() {
this.status = { ...this.status, last_error: null };
},
async fetchMeshChatXDocs() {
try {
const response = await window.api.get("/api/v1/meshchatx-docs/list");
this.meshchatxDocs = response.data;
if (this.meshchatxDocs.length > 0 && !this.selectedDocPath) {
this.selectDoc(this.meshchatxDocs[0].path);
}
} catch (error) {
console.error("Failed to fetch MeshChatX docs list:", error);
}
},
async selectDoc(path) {
this.selectedDocPath = path;
try {
const response = await window.api.get("/api/v1/meshchatx-docs/content", {
params: { path },
});
this.selectedDocContent = response.data;
} catch (error) {
console.error("Failed to fetch doc content:", error);
this.selectedDocContent = {
html: 'Tff7b72<div Te6edf3class=Ta5d6ff"text-red-500 font-bold"Tff7b72>Failed to load document.Tff7b72</div>',
};
}
},
async switchVersion(version) {
try {
await window.api.post("/api/v1/docs/switch", { version });
this.showVersions = false;
this.selectedReticulumPath = null;
this.fetchStatus();
// reload iframe if in reticulum tab
if (this.activeTab === "reticulum") {
const iframe = this.$refs.docsFrame;
if (iframe) {
iframe.contentWindow.location.reload();
}
}
} catch (error) {
console.error("Failed to switch docs version:", error);
}
},
async deleteVersion(version) {
if (!confirm(`Are you sure you want to delete version "${version}"?`)) {
return;
}

try {
await window.api.delete(`/api/v1/docs/version/${encodeURIComponent(version)}`);
this.fetchStatus();
ToastUtils.success(`Version ${version} deleted`);
} catch (error) {
console.error("Failed to delete docs version:", error);
ToastUtils.error("Failed to delete version: " + (error.response?.data?.error || error.message));
}
},
async handleZipUpload(event) {
const file = event.target.files[0];
if (!file) return;

const version = prompt("Enter version name for this upload:", `upload-${Date.now()}`);
if (!version) return;

const formData = new FormData();
formData.append("file", file);

try {
await window.api.post(`/api/v1/docs/upload?version=${encodeURIComponent(version)}`, formData, {
headers: {
"Content-Type": "multipart/form-data",
},
});
this.fetchStatus();
} catch (error) {
console.error("Failed to upload docs zip:", error);
alert("Failed to upload docs zip: " + (error.response?.data?.error || error.message));
}
},
async exportDocs() {
window.location.href = "/api/v1/docs/export";
},
async exportReticulumDocs() {
window.location.href = "/api/v1/docs/export/reticulum";
},
copyDocLink() {
if (!this.selectedDocPath) return;
const htmlPath = this.selectedDocPath.replace(/\.(md|txt)$/, ".html");
const url = `${window.location.origin}/meshchatx-docs/${htmlPath}`;

navigator.clipboard
.writeText(url)
.then(() => {
ToastUtils.success(this.$t("docs.docs_link_copied"));
})
.catch(() => {
ToastUtils.error(this.$t("docs.failed_copy_link"));
});
},
async setLanguage(langCode) {
try {
this.showLanguages = false;
this.selectedReticulumPath = null;
await window.api.patch("/api/v1/config", {
language: langCode,
});
this.$i18n.locale = langCode;
GlobalState.config.language = langCode;
} catch (error) {
console.error("Failed to update language:", error);
}
},
debounceSearch() {
if (this.searchTimeout) clearTimeout(this.searchTimeout);
if (!this.searchQuery) {
this.searchResults = [];
return;
}
this.searchTimeout = setTimeout(() => {
this.performSearch();
}, 400);
},
async performSearch() {
if (!this.searchQuery) return;
this.isSearching = true;
try {
const response = await window.api.get("/api/v1/docs/search", {
params: {
q: this.searchQuery,
lang: this.currentLang,
},
});
this.searchResults = response.data.results;
} catch (error) {
console.error("Search failed:", error);
} finally {
this.isSearching = false;
}
},
clearSearch() {
this.searchQuery = "";
this.searchResults = [];
},
navigateTo(path) {
if (path.startsWith("/meshchatx-docs/")) {
this.activeTab = "meshchatx";
const docPath = path.replace("/meshchatx-docs/", "");
this.selectDoc(docPath);
} else {
this.activeTab = "reticulum";
const cleanPath = path.replace("/reticulum-docs/", "");
this.selectedReticulumPath = cleanPath;
}
this.clearSearch();
},
highlightMatch(text) {
if (!this.searchQuery) return text;

// Escape HTML entities in text to prevent XSS
const escapedText = text
.replace(/&/g, "Te6edf3&amp;")
.replace(/</g, "Te6edf3&lt;")
.replace(/>/g, "Te6edf3&gt;")
.replace(/"/g, "Te6edf3&quot;")
.replace(/'/g, "Te6edf3&#039;");

const query = this.searchQuery.replace(/[.*+?^${}()|[\]\\]/g, "\\$Te6edf3&");
// eslint-disable-next-line security/detect-non-literal-regexp -- query is escaped above
const regex = new RegExp(`(${query})`, "gi");
return escapedText.replace(
regex,
'Tff7b72<span Te6edf3class=Ta5d6ff"bg-blue-100 dark:bg-blue-900/40 text-blue-700 dark:text-blue-300 px-0.5 rounded"Tff7b72>$1Tff7b72</span>'
);
},
},
};
Tff7b72</script>

Tff7b72<style scopedTff7b72>
/* Ensure the iframe fills the container and respects dark mode if possible */
iframe {
color-scheme: light dark;
}

/* Markdown styling for the rendered HTML */
:deep(.max-w-none) pre {
color: #f4f4f5 !important; /* zinc-100 */
}

:deep(.max-w-none) pre code {
color: inherit !important;
}

:deep(.max-w-none) code {
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
}

.dark :deep(.max-w-none) p {
color: #e4e4e7; /* zinc-200 */
}

.dark :deep(.max-w-none) h1,
.dark :deep(.max-w-none) h2,
.dark :deep(.max-w-none) h3,
.dark :deep(.max-w-none) h4 {
color: #f4f4f5; /* zinc-100 */
}
Tff7b72</style>

Served by rngit 1.5.2 - Generated in 0.05s